/**
 * Copyright © 2013 Creativor Studio.
 * About license information, please see LICENSE.txt.
 */
package org.creativor.rayson.client.transport.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.atomic.AtomicBoolean;

import org.creativor.rayson.share.transport.http.HttpResponseParser;
import org.creativor.rayson.share.transport.nio.NioConnection;
import org.creativor.rayson.share.transport.nio.ReadBuffer;
import org.creativor.rayson.share.transport.nio.WriteBuffer;

/**
 * Client side {@link NioConnection}.
 * 
 * @author creativor
 */
public class ClientNioConnection implements NioConnection {
	private static final int READ_BUFFER_SIZE = 1024 * 1024;
	private static final int WRITE_BUFFER_SIZE = 1024 * 1024;
	private final InetSocketAddress address;
	private SocketChannel socketChannel;
	private final NioListener listener;
	private SelectionKey selectionKey;
	private AtomicBoolean closed;
	private ReadBuffer readBuffer;
	private WriteBuffer writeBuffer;

	/**
	 * @param address
	 */
	public ClientNioConnection(final InetSocketAddress address,
			final NioListener listener) {
		this.address = address;
		this.listener = listener;

	}

	@Override
	public int asyncRead() throws IOException {
		final int result = readBuffer.fill();
		return result;
	}

	@Override
	public void asyncWrite() throws IOException {
		writeBuffer.asyncWrite();
	}

	@Override
	public void close() throws IOException {
		if (!closed.compareAndSet(false, true))
			return;
		if (this.socketChannel != null)
			this.socketChannel.close();

	}

	@Override
	public ReadBuffer getReadBuffer() {
		return readBuffer;
	}

	@Override
	public WriteBuffer getWriteBuffer() {
		return writeBuffer;
	}

	/**
	 * Open this connection.
	 * 
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public void open() throws IOException {
		// do connect to remote server.
		socketChannel = SocketChannel.open(this.address);
		socketChannel.configureBlocking(false);
		this.selectionKey = listener.accept(this.socketChannel, this);
		final ByteBuffer bb = ByteBuffer.allocate(READ_BUFFER_SIZE);
		final HttpResponseParser parser = new HttpResponseParser(bb);
		readBuffer = new ReadBuffer(bb, socketChannel, parser);
		writeBuffer = new WriteBuffer(WRITE_BUFFER_SIZE, socketChannel,
				selectionKey);
	}

	@Override
	public String toString() {
		final StringBuffer sb = new StringBuffer();
		sb.append("{");
		sb.append("socketChannel:" + this.socketChannel);
		sb.append(", address:" + this.address);
		sb.append("}");
		return sb.toString();
	}

}